iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0

回顧前一天講的MVC,下達request到Controller後,由Service去執行資料的 增/刪/改/查,最後再呈現在View上

在以前,我們會新增一支class繼承 HttpServlet,然後實作doGet、doPost,
除了要寫一堆if判斷request並做出對應的操作、還要負責頁面的轉導等等工作

現在有了spring boot,而且做的是前後端分離,
只要在Controller做出能讓前端好call的api就行了,做起來也相對簡單明瞭

今天就來做User的登入api
這邊我想仿作銀行的三層式帳密(ID+User+Password),

在這之前,我有在Service再補加一個verifyUser的方法,是用來驗證登入帳號密碼的,
其實就只是簡單的檢查字串,

實際上銀行的帳號密碼驗證是不會這麼陽春的,
而且傳入Server的值會在client端就先加密,
確保封包不會被側錄(end-to-end encryption),
總之很安全,有空的話就再來試著實作簡單版的ETOEE

    public String verifyUser(String email,String userAcct , String userPasswd){
        /*
         0000 login success
         0001 wrong email
         0002 wrong useracct
         0003 wrong passwd
         0004 acct locked
         9999 unknown error
         */
        Optional<User> user=userRepo.findUserByEmail(email);
        String result="9999";
        if(user.isPresent()){
            if(user.get().getUserAccount().equals(userAcct)){
                if(user.get().getUserPassword().equals(userPasswd)){
                    result="0000";
                }else{
                    result="0003";
                }
            }else{
                result="0002";
            }
        }else{
            result="0001";
        }
        return result;        
    }

本來想再加個密碼錯5次就鎖定帳號跟檢查帳號狀態,之後有時間再補充好了

接著來到controller,今天要實作登入的api

@RestController
@RequestMapping("api/user")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    @PostMapping("/login")
    public ResponseEntity<String> userLogin(@RequestBody Map<String,String> map){
        String email= map.get("email");
        String userAccount= map.get("userAccount");
        String userPassword= map.get("userPassword");
        String result="null data";
        if(StringUtils.isBlank(email)||StringUtils.isBlank(userAccount)||StringUtils.isBlank(userPassword)){
            return ResponseEntity.ok(result);
        }else{
            result=userService.verifyUser(email, userAccount, userPassword);
            return ResponseEntity.ok(result);  
        }
    }   
    
}

在這邊我用了5個annotation:

@RestController用來告訴Spring這是一個RestController,

@RestController@Controller的區別是
@RestController下的方法return的值會直接被預設為Response body回傳
@RestController也等於是@Controller+@ResponseBody

@RestController適用於api這樣直接回傳值的需求
@Controller則是適合用在jsp、thymeleaf這種Server同時也負責頁面渲染的情況

接著,當Spring知道這是一個Controller後就會去檢查有沒有@RequestMapping
並將這個Controller綁在相對的url上,像我設定的是"api/user",
因此會對應到的url是 localhost:8080/api/user

@RequiredArgsConstructor跟昨天在Service的用法一樣是用來實體化物件用的就不贅述

@PostMapping應該很好理解
就是對應瀏覽器的HTTP method的實作

相關的有:
@GetMapping
@PostMapping
@DeleteMapping
@PutMapping
@PatchMapping

我在這邊決定以POST方法來實做登入功能,
@PostMapping("/login")對應到 localhost:8080/api/user/login

    @PostMapping("/login")
    public ResponseEntity<String> userLogin(@RequestBody HashMap<String,String> map){
        String email= map.get("email");
        String userAccount= map.get("userAccount");
        String userPassword= map.get("userPassword");
        String result="請輸入帳號密碼!";
        if(StringUtils.isBlank(email)||StringUtils.isBlank(userAccount)||StringUtils.isBlank(userPassword)){
            return ResponseEntity.ok(result);
        }else{
            result=userService.verifyUser(email, userAccount, userPassword);
            return ResponseEntity.ok(result);  
        }
    } 

@RequestBody會接收Post發送過來的request Body,我選擇以HashMap來儲存,
(其實應該要用User這個Object來存較好,
用HashMap的話除了無法對傳入的值做最低限度的限制,也降低程式碼的可讀性)

Spring會將requestBody轉為Map的形式傳入

雖然到時候在前端會檢查,不過我在這邊還是有做一下null檢核,

org.apache.commons.lang3.StringUtils,好用!
要記得在pom.xml加個dependency

		<dependency>
    		<groupId>org.apache.commons</groupId>
   			<artifactId>commons-lang3</artifactId>
   		 	<version>3.9</version>
		</dependency>

本來在想登入成功跟失敗的Response Code是不是要有區別比較好,不過還沒想到前端到時候會怎麼做
就決定先全部都回200,也就是ok

在測試api時發現post方法一直回傳"status":403,"error":"Forbidden"
https://ithelp.ithome.com.tw/upload/images/20210921/20128973489ebywS22.png

查了一下發現原來是因為Spring Security的預設設定有CSRF的保護
會阻擋沒帶CSRF token的POST、PATCH、PUT、DELETE等http method

為求測試方便,先把這個功能關閉,順便也關掉Spring Security的登入頁

建立一個WebSecurityConfig.java,

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().permitAll() //讓所有頁面都不用登入
            .and().csrf().disable(); //關掉csrf保護
    }  
}

接著再測就成功把post送到server了
https://ithelp.ithome.com.tw/upload/images/20210921/201289734d9OStflWW.png

回0000,表示帳號密碼正確

今天就先這樣吧!


上一篇
[Day 05] - 用Spring Boot 建立Service
下一篇
[Day 07] - Spring Boot 實作登入驗證(一)(TOKEN or SESSION?)
系列文
30天全端挑戰!React+Spring Boot+Mongo DB 串接永豐API 打造金融網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言